export default class KMeans {
  /**
   * K-Means聚类算法
   * @param {Array} data arrary 数据数组
   * @param {num} num 分段数目
   */
  constructor(data, num) {
    this.center = new Array(); //   中心链表
    this.cluster = new Array(); //   归类
    this.Jc = new Array(); //   误差平方和
    this.result = new Array(); //   误差平方和

    let timeData = data; //   当前的数据
    let randoms = new Array(); //   保存的随机数（0~data.length之间）
    let flag;

    // 如果数据的数目小于分段树要补零
    if (data.length <= num) {
      let re = [];
      for (let i = 0; i < num - data.length + 1; i++) {
        re.push(0);
      }
      for (let j = 0; j < data.length; j++) {
        re.push(data[j]);
      }

      timeData = re; // 重新赋值
    }
    // num = num + 1; //   num段数据需要num+1个数据，故+1
    // 初始化鏃中心
    let tempNum = Math.floor(Math.random() * num);
    for (let i = 0; i < num; i++) {
      flag = true;
      while (flag) {
        tempNum = Math.floor(Math.random() * timeData.length);

        let isExist = false; // 判断随机数是否存在
        for (let j = 0; j < i; j++) {
          // 如果当前生成的随机数已经存在，不添加
          if (tempNum === randoms[j]) {
            isExist = true;
            break;
          }
        }
        if (!isExist) {
          flag = false;
        }
      }
      randoms[i] = tempNum;
      this.center[i] = timeData[randoms[i]];
    }

    // 初始化鏃
    this.initCluster(num);

    let m = 0;
    while (true) {
      this.clusterSet(num, timeData, this.center); // 生成簇集元素
      this.countRule(); // 计算误差平方和
      // 判断退出迭代条件
      if (m != 0) {
        if (this.Jc[m] - this.Jc[m - 1] == 0) {
          break;
        }
      }
      this.findNewCenter(num); // 计算新的中心
      m++;
      this.cluster = this.initCluster(num); // 簇集初始化
    }
    let temp = new Array();
    for (let no = 0; no < num; no++) {
      temp.push(this.cluster[no][0]);
    }
    temp.sort(this.sortOnValueRang); // 降序
    this.result = temp;
    this.result.sort(function (a, b) {
      return a - b;
    });
  }
}
KMeans.prototype = {
  //  鏃初始化
  initCluster(num) {
    let tmp = new Array();
    let clustertmp = new Array();
    for (let i = 0; i < num; i++) {
      clustertmp.push(tmp);
    }
    return clustertmp;
  },
  /**
   * 生产鏃元素
   * @param num
   *          段数
   * @param timeData
   *          原始数据集
   * @param center
   *          随机中心数
   */
  clusterSet(num, timeData, center) {
    let clustertmp = new Array();

    let distance = new Array(num);
    let clusterseat = new Array();
    for (let i = 0; i < timeData.length; i++) {
      //  计算timeData[i]与各个中心点的距离
      for (let j = 0; j < num; j++) {
        distance[j] = this.distancetmp(timeData[i], center[j]);
      }
      //  最小值所在的位置
      let minLocation = this.minDistance(distance, num);
      clusterseat.push(minLocation, timeData[i]);
    }

    //  初始化二维数组
    for (let ii = 0; ii < num; ii++) {
      clustertmp.push([]);
    }
    //  归一类
    for (let sum = 0; sum < clusterseat.length; sum += 2) {
      //  填充数据
      clustertmp[clusterseat[sum]].push(clusterseat[sum + 1]);
    }
    this.cluster = clustertmp;
  },
  /**
   * 计算两个数之间的距离
   *
   * @param element
   *          当前元素
   * @param center
   *          当前元素中心数
   * @returns {number}
   */
  distancetmp(element, center) {
    let x = element - center;
    let z = x * x;
    return Math.sqrt(z);
  },

  /**
   * 计算误差平方和准则函数方法
   */
  countRule() {
    let JcF = 0;
    for (let i = 0; i < this.cluster.length; i++) {
      for (let j = 0; j < this.cluster[i].length; j++) {
        JcF += this.errorSquare(this.cluster[i][j], this.center[i]);
      }
    }
    this.Jc.push(JcF);
  },

  /**
   * 求误差平方的方法(求两个数的距离的平方)
   * @param element
   * @param center
   * @returns {number}
   */
  errorSquare(element, center) {
    let x = element - center;
    let errorSquare = x * x;
    return errorSquare;
  },

  /**
   * 求最小距离（位置/下表）方法
   * @param distance
   *          当前元素距离中心的最小值
   * @param num
   *          分段个数
   */
  minDistance(distance, num) {
    let minDistance = distance[0];
    let minLocation = 0;
    for (let i = 1; i < distance.length; i++) {
      if (distance[i] < minDistance) {
        minDistance = distance[i];
        minLocation = i;
      } else if (distance[i] == minDistance) {
        //  如果和当前最短距离相等，则随机选取一个
        if (Math.floor(Math.random() * num) < 5) {
          minLocation = i;
        }
      }
    }
    return minLocation;
  },

  /**
   * 计算新的簇中心方法
   * <p>
   *     说明：每一簇的所有点的中心为新的处
   * </p>
   * @param num
   *          分段数
   */
  findNewCenter(num) {
    for (let i = 0; i < num; i++) {
      let n = this.cluster[i].length;
      if (n != 0) {
        let newCenter = 0; //  默认为0
        for (let j = 0; j < n; j++) {
          newCenter += this.cluster[i][j];
        }
        newCenter = parseFloat(newCenter / n);
        this.center[i] = newCenter;
      }
    }
  },
  /**
   * 降序排列
   * @param a
   * @param b
   * @returns {number}
   */
  sortOnValueRang(a, b) {
    let val1 = a;
    let val2 = b;

    if (val1 < val2) {
      return 1;
    } else if (val1 > val2) {
      return -1;
    } else {
      return 0;
    }
  }
};
